home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Aminet 4
/
Aminet 4 - November 1994.iso
/
aminet
/
comm
/
term
/
xprkermit_2.lha
/
ckxtio.c
< prev
next >
Wrap
C/C++ Source or Header
|
1993-05-17
|
23KB
|
830 lines
char *ckxv = "XPR tty I/O, 5A(001), 23 Apr 92";
/*
* C K X T I O
*
* Terminal I/O functions for the Amiga External Protocol (XPR) version
* of C Kermit.
*/
#include "ckcdeb.h"
#include "ckcker.h"
#include "ckxker.h"
#include "ckxtim.h"
extern int local, what, spackets, fsecs, rpktl, spktl, bctu, numerrs, timeouts;
extern long ffc, tsecs;
extern long (*xupdate) (), (*xswrite) (), (*xfopen) (), (*xfclose) (),
(*xfread) (), (*xsread) (), (*xchkabort) (void), (*xfnext) (), (*xffirst) (),
(*xsflush) (void), (*xfwrite) (), (*xgets) (), (*xfinfo) (), (*xunlink)() ,
(*xsquery) (void), (*xchkmisc) (void), (*xsetserial)();
int
ttchk() {
if (xsquery != NULL)
return((*xsquery)());
else
return 0;
}
int
ttclos(int ignore) {
return 0;
}
int
ttfluo(void) {
return((*xsflush)());
}
int
ttgmdm(void) {
return -3;
}
long
ttgspd(void) {
int x;
x = ((calld(xsetserial, -1L) >> 16) & 255);
switch(x) {
case 0:
return(110L);
case 1:
return(300L);
case 2:
return(1200L);
case 3:
return(2400L);
case 4:
return(4800L);
case 5:
return(9600L);
case 6:
return(19200L);
case 8:
return(38400L);
case 9:
return(57600L);
case 10:
return(76800L);
case 11:
return(115200L);
default:
return -1;
}
}
int
tthang(void) {
return 0;
}
void
ttimoff(void) {
}
#ifdef MYREAD
/* Private buffer for myread() and its companions. Not for use by anything
* else. ttflui() is allowed to reset them to initial values. ttchk() is
* allowed to read my_count.
*
* my_item is an index into mybuf[]. Increment it *before* reading mybuf[].
*
* A global parity mask variable could be useful too. We could use it to
* let myread() strip the parity on its own, instead of stripping sign
* bits as it does now.
*/
#define MYBUFLEN 256
static CHAR mybuf[MYBUFLEN];
static int my_count = 0; /* Number of chars still in mybuf */
static int my_item = -1; /* Last index read from mybuf[] */
/* myread() -- Efficient read of one character from communications line.
*
* Uses a private buffer to minimize the number of expensive read() system
* calls. Essentially performs the equivalent of read() of 1 character, which
* is then returned. By reading all available input from the system buffers
* to the private buffer in one chunk, and then working from this buffer, the
* number of system calls is reduced in any case where more than one character
* arrives during the processing of the previous chunk, for instance high
* baud rates or network type connections where input arrives in packets.
* If the time needed for a read() system call approaches the time for more
* than one character to arrive, then this mechanism automatically compensates
* for that by performing bigger read()s less frequently. If the system load
* is high, the same mechanism compensates for that too.
*
* myread() is a macro that returns the next character from the buffer. If the
* buffer is empty, mygetbuf() is called. See mygetbuf() for possible error
* returns.
*
* This should be efficient enough for any one-character-at-a-time loops.
* For even better efficiency you might use memcpy()/bcopy() or such between
* buffers (since they are often better optimized for copying), but it may not
* be worth it if you have to take an extra pass over the buffer to strip
* parity and check for CTRL-C anyway.
*
* Note that if you have been using myread() from another program module, you
* may have some trouble accessing this macro version and the private variables
* it uses. In that case, just add a function in this module, that invokes the
* macro.
*/
#define myread() (--my_count < 0 ? mygetbuf() : 255 & (int)mybuf[++my_item])
/* Specification: Push back up to one character onto myread()'s queue.
*
* This implementation: Push back characters into mybuf. At least one character
* must have been read through myread() before myunrd() may be used. After
* EOF or read error, again, myunrd() can not be used. Sometimes more than
* one character can be pushed back, but only one character is guaranteed.
* Since a previous myread() must have read its character out of mybuf[],
* that guarantees that there is space for at least one character. If push
* back was really needed after EOF, a small addition could provide that.
*
* myunrd() is currently not called from anywhere inside kermit...
*/
#ifdef NOTUSED
myunrd(ch) CHAR ch; {
if (my_item >= 0) {
mybuf[my_item--] = ch;
++my_count;
}
}
#endif
/* mygetbuf() -- Fill buffer for myread() and return first character.
*
* This function is what myread() uses when it can't get the next character
* directly from its buffer. First, it calls a system dependent myfillbuf()
* to read at least one new character into the buffer, and then it returns
* the first character just as myread() would have done. This function also
* is responsible for all error conditions that myread() can indicate.
*
* Returns: When OK => a positive character, 0 or greater.
* When EOF => -2.
* When error => -3, error code in errno.
*
* Older myread()s additionally returned -1 to indicate that there was nothing
* to read, upon which the caller would call myread() again until it got
* something. The new myread()/mygetbuf() always gets something. If it
* doesn't, then make it do so! Any program that actually depends on the old
* behaviour will break.
*
* The older version also used to return -2 both for EOF and other errors,
* and used to set errno to 9999 on EOF. The errno stuff is gone, EOF and
* other errors now return different results, although Kermit currently never
* checks to see which it was. It just disconnects in both cases.
*
* Kermit lets the user use the quit key to perform some special commands
* during file transfer. This causes read(), and thus also mygetbuf(), to
* finish without reading anything and return the EINTR error. This should
* be checked by the caller. Mygetbuf() could retry the read() on EINTR,
* but if there is nothing to read, this could delay Kermit's reaction to
* the command, and make Kermit appear unresponsive.
*
* The debug() call should be removed for optimum performance.
*/
int myfillbuf(void);
int
mygetbuf(void) {
my_count = myfillbuf();
debug(F101, "myfillbuf read", "", my_count);
if (my_count <= 0)
return(my_count < 0 ? -3 : -2);
--my_count;
return(255 & (int)mybuf[my_item = 0]);
}
/* myfillbuf():
* System-dependent read() into mybuf[], as many characters as possible.
*
* Returns: OK => number of characters read, always more than zero.
* EOF => 0
* Error => -1, error code in errno.
*
* If there is input available in the system's buffers, all of it should be
* read into mybuf[] and the function return immediately. If no input is
* available, it should wait for a character to arrive, and return with that
* one in mybuf[] as soon as possible. It may wait somewhat past the first
* character, but be aware that any such delay lengthens the packet turnaround
* time during kermit file transfers. Should never return with zero characters
* unless EOF or irrecoverable read error.
*
* Correct functioning depends on the correct tty parameters being used.
* Better control of current parameters is required than may have been the
* case in older Kermit releases. For instance, O_NDELAY (or equivalent) can
* no longer be sometimes off and sometimes on like it used to, unless a
* special myfillbuf() is written to handle that. Otherwise the ordinary
* myfillbuf()s may think they have come to EOF.
*
* If your system has a facility to directly perform the functioning of
* myfillbuf(), then use it. If the system can tell you how many characters
* are available in its buffers, then read that amount (but not less than 1).
* If the system can return a special indication when you try to read without
* anything to read, while allowing you to read all there is when there is
* something, you may loop until there is something to read, but probably that
* is not good for the system load.
*/
/*
* Following the example of the Unix code, I have chosen to write the XPR
* version as follows: if the xpr_squery exists, then it is called.
* If it does not exist, or returns 0, then we call xsread of 1 character
* with a one-second timeout. Thus, we simply return 0 if no characters
* arrive in one second, at which point we recheck our overall timeout.
*/
int
myfillbuf(void) {
long avail;
int x;
if (xsquery)
avail = (*xsquery)();
if (!xsquery || avail == 0)
avail = 1;
if (avail > MYBUFLEN)
avail = MYBUFLEN;
return(calladd(xsread, mybuf, avail, 1000000L));
}
#endif /* MYREAD */
/* T T F L U I -- Flush tty input buffer */
int
ttflui() {
#ifdef MYREAD
/*
Flush internal MYREAD buffer *FIRST*, in all cases.
*/
my_count = 0; /* Reset count to zero */
my_item = -1; /* And buffer index to -1 */
#endif /* MYREAD */
if (xsflush)
(void) (*xsflush)();
return(0);
}
/*
* T T I N L
*
* Return a line in the buffer pointed to by s from the serial line.
* The line must end with eol and be no longer than max characters.
* timeout is a timeout interval in seconds.
*
* Returns
* > 0 Success--number of characters read
* = 0 Shouldn't happen!
* -1 Timeout
* -2 Error of some other kind
*
* This version for XPR Kermit. We ignore negative returns from
* the xpr_sread() function, preferring to take care of these
* via the timeout mechanism.
*/
int ttinl(CHAR *s, int max, int timeout, CHAR eol)
{
int x = 0;
long i;
struct timerequest *Timereq;
unsigned mask;
extern int parity;
if (xchkmisc)
(void) (*xchkmisc)();
*s = '\0';
mask = (parity ? 0177 : 0377);
/*
* Set up and post timer request for overall timeout.
* As a compromise between correct and efficient, I only check
* the overall timeout if a one-character read with a one second
* timeout fails first.
*/
if (!(Timereq = CreateTimer(UNIT_VBLANK)))
return -1;
Timereq->tr_time.tv_secs = timeout;
Timereq->tr_time.tv_micro = 0;
Timereq->tr_node.io_Command = TR_ADDREQUEST;
SendIO((struct IORequest *) Timereq);
#ifndef MYREAD
/*
* Willy Langeveld assures me that, since serial.device does its
* own buffering, calling xsread once for each character isn't
* going to take too long.
*
*/
for (x = 0; x < max; ) {
if ((i = calladd(xsread, s, 1L, 1000000L)) < 0) {
if (chkint() < 0) {
x = -2;
goto leave;
}
} else if (i == 1) {
x++;
if ((*s++ &= mask) == eol)
break;
} else if (CheckIO((struct IORequest *) Timereq)) { /* timeout */
x = -1; /* Flag timeout abort */
goto leave;
}
}
*s = '\0'; /* Normal termination */
leave: /* Common cleanup code */
AbortIO((struct IORequest *) Timereq);
WaitIO((struct IORequest *) Timereq);
DeleteTimer(Timereq);
return(x);
#else
/*
* Despite the comment above, the myread() macro significantly improves
* performance on terminal emulators whose xpr_sread() is not as finely
* tuned, apparently as VLT's. Term 3.n comes to mind...
*/
for (x = 0; x < max; ) {
if ((i = myread()) < -1) {
/*
* The following code deals with the case that
* xsread() returns a negative value if the
* user has clicked on the "Cancel File" or
* "Cancel Batch" gadgets. However, we don't
* want to abort receiving the current packet
* in this case; calling chkint() now sets
* the global cx and/or cz flags, but in the
* case of a complete abort, chkint() returns
* a negative value.
*/
if (chkint() < 0) {
x = -2; /* Error */
goto leave;
} else if (CheckIO((struct IORequest *) Timereq)) { /* timeout */
x = -1; /* Flag timeout abort */
goto leave;
}
} else if (i >= 0) { /* Successful get of character */
x++;
*s = i;
if ((*s++ &= mask) == eol)
break;
}
}
*s = '\0'; /* Normal termination */
leave: /* Common cleanup code */
AbortIO((struct IORequest *) Timereq);
WaitIO((struct IORequest *) Timereq);
DeleteTimer(Timereq);
if (chkint() < 0)
return -2;
return(x);
#endif /*MYREAD*/
}
int
ttinc(int ttimo) {
#ifndef MYREAD
unsigned char ch;
#else
int n;
#endif /*MYREAD*/
#ifdef MYREAD
n = myread();
return (n < 0 ? n : n & 0377);
#else
if (calladd(xsread, &ch, 1, ttimo*1000000L) <= 0)
return -1;
return(ch);
#endif /*MYREAD*/
}
int
ttoc(char c) {
if (callad(xswrite, &c, 1) == 0L)
return 1;
else
return -1;
}
int
ttol(CHAR *s, int n) {
if (callad(xswrite, s, n) == 0L)
return n;
else
return -1;
}
int
ttopen(char *ttname, int *lcl, int modem, int timo) {
return 0;
}
int
ttpkt(long speed, int flow, int parity) {
return 0;
}
int
ttres(void) {
return 0;
}
int
ttscarr(int carrier) {
return carrier;
}
int
ttsndb(void) {
return 0;
}
int
ttsndlb(void) {
return 0;
}
int
ttsspd(int cps) {
return 0;
}
int
ttvt(long speed, int flow) {
return 0;
}
int
ttwmdm(int mdmsig, int timo) {
return -3;
}
int
ttxin(int n, CHAR *buf) {
#ifdef MYREAD
int c, i;
#endif
#ifdef MYREAD
for (i = 0; i < n; i++) {
c = myread();
if (c < 0)
return i;
*buf++ = c & 0377;
}
return n;
#else
return(calladd(xsread, buf, n, 0L));
#endif /*MYREAD*/
}
void
ztime(char **s) {
*s = "Ddd Mmm 00 00:00:00 0000\n"; /* Return dummy in asctime() format */
}
int
msleep(int m) {
struct timeval tv;
tv.tv_secs = m/1000;
tv.tv_micro = (m % 1000)*1000;
return(MyDelay(&tv, 0L));
}
int
sleep(int m) {
return(msleep(1000*m));
}
static struct timeval now, elapsed;
void
rtimer(void) {
GetSysTime(&now);
}
int
gtimer(void) {
GetSysTime(&elapsed);
return((((1000000L*elapsed.tv_secs + elapsed.tv_micro) -
(1000000L*now.tv_secs + now.tv_micro)) + 500000L)/1000000L);
}
int
sysinit(void) {
return 0;
}
int
syscleanup(void) {
return 0;
}
void
connoi(void) {
}
int
conoll(char *s) {
return 0;
}
int
conol(char *s) {
return 0;
}
int
conoc(char c) {
return 1;
}
static int oldtyp = 0, olderrs = -1, oldlen = -1, oldffc = 0L, oldtimes = -1;
char * /* Convert seconds to hh:mm:ss */
hhmmss(long x)
{
static char buf[10];
long s, h, m;
h = x / 3600L; /* Hours */
x = x % 3600L;
m = x / 60L; /* Minutes */
s = x % 60L; /* Seconds */
if (x > -1L)
sprintf(buf,"%02ld:%02ld:%02ld",h,m,s);
else buf[0] = '\0';
return((char *)buf);
}
/*
* S C R E E N
*
* Display status on the screen. This version is the go-between from
* what C Kermit needs and what XPR provides.
*/
void
screen(int f, char c, long n, char *s) {
static struct XPR_UPDATE update;
static char string[51], time1[15], time2[15];
static long fsiz = -1L; /* Copy of file size */
static long fcnt = 0L; /* File count */
static long fbyt = 0L; /* Total file bytes */
static char fn[257]; /* Local copy of file name */
update.xpru_updatemask = 0;
debug(F101,"screen f = ","",f); /* Handle our function code */
if (n) debug(F101, "screen n = ", "", n);
if (s) debug(F110, "screen s = ", s, n);
switch(f) {
case SCR_FN:
if (what == W_SEND)
sprintf(string, "Sending: %s", s);
else
sprintf(string, "Receiving: %s", s);
update.xpru_filename = string;
update.xpru_updatemask = XPRU_FILENAME;
fsiz = -1L; /* Invalidate file size */
strncpy(fn, s, 256);
break;
case SCR_AN:
if (what == W_SEND)
sprintf(string, "Sending: %s as %s", fn, s);
else
sprintf(string, "Receiving: %s as %s", fn, s);
update.xpru_filename = string;
update.xpru_updatemask = XPRU_FILENAME;
break;
case SCR_FS:
fsiz = update.xpru_filesize = n;
update.xpru_updatemask = XPRU_FILESIZE;
break;
case SCR_ST:
switch (c) { /* Print new status message */
case ST_OK: /* Transfer OK */
fcnt++; /* Count this file */
if (ffc > 0) /* For some reason ffc is off by 1 */
fbyt += ffc - 1L; /* Count its bytes */
strcpy(string, "Transfer OK");
break;
case ST_DISC:
strcpy(string, "File discarded");
break;
case ST_INT:
strcpy(string, "Transfer interrupted");
break;
case ST_SKIP:
strcpy(string, "File skipped");
break;
case ST_ERR:
sprintf(string, "%.50s", s);
break;
case ST_REFU:
strcpy(string, "File refused");
break;
case ST_INC:
strcpy(string, "Incompletely received");
break;
default:
strcpy(string, "screen() called with bad status");
break;
}
if (c == ST_OK) {
update.xpru_updatemask = XPRU_MSG;
update.xpru_msg = string;
} else {
update.xpru_updatemask = XPRU_ERRORMSG;
update.xpru_errormsg = string;
}
break;
case SCR_PN:
update.xpru_blocks = n;
update.xpru_updatemask = XPRU_BLOCKS;
break;
case SCR_PT:
/*
* Following the Unix version, we check everything and only
* update what's actually changed since last time. The intent
* is to prevent the screen updates from slowing the file
* transfers.
*/
update.xpru_updatemask = 0;
if (spackets < 5) {
switch (bctu) {
case 1:
update.xpru_blockcheck = "Sum-6";
break;
case 2:
update.xpru_blockcheck = "Sum-12";
break;
case 3:
update.xpru_blockcheck = "CRC-16";
break;
}
update.xpru_updatemask |= XPRU_BLOCKCHECK;
}
if ((update.xpru_blocksize = (what == W_RECV) ? rpktl + 1 : spktl +
1) != oldlen) {
update.xpru_updatemask |= XPRU_BLOCKSIZE;
oldlen = update.xpru_blocksize;
}
update.xpru_blocks = spackets;
update.xpru_updatemask |= XPRU_BLOCKS;
if (c != oldtyp && c != 'Y' && c != 'N' && c != '%') {
oldtyp = update.xpru_packettype = c;
update.xpru_updatemask |= XPRU_PACKETTYPE;
}
if (numerrs != olderrs) {
olderrs = update.xpru_errors = numerrs;
update.xpru_updatemask |= XPRU_ERRORS;
}
if (ffc != oldffc) {
oldffc = update.xpru_bytes = ffc;
update.xpru_updatemask |= XPRU_BYTES;
}
switch (c) { /* Now handle specific packet types */
long x, y;
case 'S': /* Beginning of transfer */
fcnt = fbyt = 0L; /* Clear counters */
break;
case 'D': /* Data packet */
/* fsecs is the time from gtimer() this file started at */
y = ((unsigned) gtimer() - fsecs); /* Secs so far */
strcpy(time1, hhmmss(y));
update.xpru_elapsedtime = time1;
update.xpru_updatemask |= XPRU_ELAPSEDTIME;
if (y > 0) {
update.xpru_datarate = ((10L * ffc)/y)/10L;
update.xpru_updatemask |= XPRU_DATARATE;
}
if (y > 0L && fsiz > 0L && ffc > 0L) { /* Update expected time so on */
x = (y*fsiz)/ffc;
strcpy(time2, hhmmss(x));
update.xpru_expecttime = time2;
update.xpru_updatemask |= XPRU_EXPECTTIME;
}
break;
case 'E': /* Error packet */
if (*s) { /* Print its data field */
update.xpru_errormsg = s;
update.xpru_updatemask |= XPRU_ERRORMSG;
}
fcnt = fbyt = 0; /* No bytes for this file */
break;
case 'Q':
update.xpru_errormsg = "Damaged packet";
update.xpru_updatemask |= XPRU_ERRORMSG;
break;
case 'T':
update.xpru_errormsg = "Timeout";
update.xpru_updatemask |= XPRU_ERRORMSG;
if ((update.xpru_timeouts = timeouts) != oldtimes) {
update.xpru_updatemask |= XPRU_TIMEOUTS;
oldtimes = timeouts;
}
break;
default:
break;
}
break;
case SCR_TC:
if (tsecs > 0)
sprintf(string, "Files: %ld, Total Bytes: %ld, %ld cps",
fcnt, fbyt, ((fbyt * 10L) / (long) tsecs) / 10L);
else
sprintf(string, "Files: %ld, Total Bytes: %ld", fcnt, fbyt);
update.xpru_msg = string;
update.xpru_updatemask = XPRU_MSG;
break;
case SCR_EM:
update.xpru_errormsg = s;
update.xpru_updatemask = XPRU_ERRORMSG;
break;
case SCR_QE: /* Quantity equals */
case SCR_TU: /* Undelmited text */
case SCR_TN: /* Text delimited at start */
case SCR_TZ: /* Text delimited at end */
break;
case SCR_XD: /* X packet data */
update.xpru_filename = s;
update.xpru_updatemask = XPRU_FILENAME;
break;
case SCR_CW: /* Close window */
oldtyp = 0; /* Reset these counters for next time */
olderrs = oldtimes = oldlen = -1;
oldffc = 0;
break;
default:
break;
}
if (update.xpru_updatemask) (void) calla(xupdate, &update);
return;
}
/*
* This is the XPR Kermit version of chkint. We allow multiple levels
* of interrupt, if available.
*/
int
chkint(void) { /* Check for console interrupts. */
int x;
extern int cxseen, czseen;
x = (*xchkabort)();
switch (x) {
case 0: /* No abort pending */
return 0;
case 1: /* Abort file only. */
screen(SCR_TN,0,0l,"Cancelling File ");
cxseen = 1;
break;
case 2: /* Abort file and batch. */
screen(SCR_TN,0,0l,"Cancelling Batch ");
czseen = 1;
break;
case -1: /* Emergency escape -- kill all */
return -1;
default:
;
}
return 1;
}
void
intmsg(long n) {
screen(SCR_TN, 0, 0L, "Interrupt using mouse or keyboard");
}
void
ermsg(msg) char *msg; { /* Print error message */
if (local)
screen(SCR_EM,0,0L,msg);
tlog(F110,"Protocol Error:",msg,0L);
}
void
fatal(char *msg) {
if (local)
screen(SCR_EM,0,0L,msg);
tlog(F110,"Fatal Error:",msg,0L);
}
void
doclean(void) {
}
int
psuspend(int x) {
return 0;
}